home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d8 / pdriver5.arc / TRACE.ASM < prev    next >
Assembly Source File  |  1989-12-17  |  20KB  |  950 lines

  1. version    equ    0
  2.  
  3.     include    defs.asm
  4.  
  5. ;  Copyright, 1988, 1989, Russell Nelson
  6.  
  7. ;   This program is free software; you can redistribute it and/or modify
  8. ;   it under the terms of the GNU General Public License as published by
  9. ;   the Free Software Foundation, version 1.
  10. ;
  11. ;   This program is distributed in the hope that it will be useful,
  12. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. ;   GNU General Public License for more details.
  15. ;
  16. ;   You should have received a copy of the GNU General Public License
  17. ;   along with this program; if not, write to the Free Software
  18. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. code    segment byte public
  21.     assume    cs:code, ds:code
  22.  
  23.     org    2ch
  24. phd_env    label    word
  25.  
  26.     org    5ch
  27. phd_fcb1    label    byte
  28.  
  29.     org    6ch
  30. phd_fcb2    label    byte
  31.  
  32.     org    80h
  33. phd_dioa    label    byte
  34.  
  35.     org    100h
  36. start:
  37.     jmp    start_1
  38.  
  39. stack    label    byte
  40.  
  41. comment /
  42.  
  43. Plan:
  44.  
  45. Keep a circular queue of events.  The size of the queue is a settable
  46. parameter.  Discard events that fall off the end.  Remember how many events
  47. were discarded.  Remember when the events occurred.
  48.  
  49. Type of events to remember:
  50.     calls to the packet driver
  51.     upcalls to the receiver handler (both kinds)
  52.  
  53. /
  54.  
  55. ; In addition to the function numbers specified in the FTP Software
  56. ; packet driver spec, the following pseudo-functions are defined:
  57.  
  58. EVENT_RECEIVE    equ    255        ;receiver upcall.
  59.  
  60. event_struc    struc
  61. event_length    dw    ?        ;length of this event.
  62. event_function    db    ?        ;the event function number.
  63. event_time    dw    ?,?        ;timer tick at the time of the call.
  64. event_error    db    ?        ;set to value of dh after the SWI.
  65. event_struc    ends
  66.  
  67. di_struc    struc
  68.         db    (size event_struc) dup (?)
  69. di_version    dw    ?
  70. di_class    db    ?
  71. di_type        dw    ?
  72. di_number    db    ?
  73. di_basic    db    ?
  74. di_struc    ends
  75.  
  76. at_struc    struc
  77.         db    (size event_struc) dup (?)
  78. at_if_class    db    ?
  79. at_if_type    dw    ?
  80. at_if_number    db    ?
  81. at_typelen    dw    ?
  82. at_handle    dw    ?
  83. at_struc    ends
  84.  
  85. handle_struc    struc
  86.         db    (size event_struc) dup (?)
  87. event_handle    dw    ?
  88. handle_struc    ends
  89.  
  90. ga_struc    struc
  91.         db    (size event_struc) dup (?)
  92. ga_handle    dw    ?
  93. ga_length    dw    ?
  94. ga_struc    ends
  95.  
  96.  
  97. queue_length    dw    10000,?        ;length of the queue.
  98. queue_tail    dw    ?        ;points after the last item in the queue.
  99. queue_ptr    dw    ?        ;points to the new item in the queue.
  100. queue_head    dw    ?        ;points to the first item in the queue.
  101. queue_end    dw    ?        ;points to the end of the queue, but
  102.                     ;there is room for one more event after
  103.                     ;this one.
  104.  
  105. packet_int_no    db    ?,0,0,0
  106.  
  107. parm    dw    0
  108. parm2    dw    their_dioa,?
  109. parm3    dw    phd_fcb1,?
  110. parm4    dw    phd_fcb2,?
  111.  
  112. comspec_env_str    db    "COMSPEC="
  113. comspec_env_len    equ    $-comspec_env_str
  114.  
  115. program        db    64 dup(?)
  116. their_dioa    db    0,0dh,128-2 dup(?)
  117.  
  118. saved_ax    label    word
  119. saved_al    db    ?
  120. saved_ah    db    ?
  121. saved_bx    dw    ?
  122. saved_ds    dw    ?
  123. saved_f        dw    ?
  124.  
  125. functions    label    word
  126.     dw    f_driver_info        ;function 1
  127.     dw    f_access_type
  128.     dw    f_release_type
  129.     dw    f_send_pkt
  130.     dw    f_terminate
  131.     dw    f_get_address
  132.     dw    f_reset_interface    ;function 7
  133.     dw    f_set_rcv_mode        ;function 20
  134.     dw    f_get_rcv_mode
  135.     dw    set_multicast_list
  136.     dw    get_multicast_list
  137.     dw    f_get_statistics
  138.     dw    f_set_address        ;function 25
  139.  
  140. their_isr    dd    ?
  141.  
  142. our_isr:
  143.     jmp    our_isr_0        ;the required signature.
  144. signature    db    'PKT DRVR',0
  145. signature_len    equ    $-signature
  146.  
  147. our_isr_0:
  148.     assume    ds:nothing
  149.     mov    saved_ds,ds
  150.     mov    saved_bx,bx
  151.     mov    saved_ax,ax
  152.     cld
  153.  
  154.     mov    bx,sp
  155.     mov    bx,ss:[bx+4]        ;get the original flags.
  156.     mov    saved_f,bx
  157.  
  158.     mov    bx,cs            ;set up ds.
  159.     mov    ds,bx
  160.     assume    ds:code
  161.     mov    bl,ah            ;jump to the correct function.
  162.     mov    bh,0
  163.     cmp    bx,7            ;highest function is 7.
  164.     jbe    our_isr_3
  165.     cmp    bx,20
  166.     jb    our_isr_bad
  167.     cmp    bx,25
  168.     ja    our_isr_bad
  169.     sub    bx,20-7-1        ;map 20 right after 7.
  170. our_isr_3:
  171.     add    bx,bx            ;*2
  172.     jmp    functions-2[bx]        ;table starts at 1.
  173.  
  174. our_isr_bad:
  175.     call    do_their_isr
  176.     jmp    our_isr_done
  177.  
  178. f_driver_info:
  179.     mov    bx,(size di_struc)
  180.     call    queue_advance
  181.     call    do_their_isr
  182.     jc    f_driver_info_1
  183.  
  184.     mov    ax,saved_bx
  185.     mov    [bx].di_version,ax
  186.     mov    [bx].di_class,ch
  187.     mov    [bx].di_type,dx
  188.     mov    [bx].di_number,cl
  189.     mov    al,saved_al
  190.     mov    [bx].di_basic,al
  191. ;we ignore the name -- too much work.
  192. f_driver_info_1:
  193.     jmp    our_isr_done
  194.  
  195. f_get_statistics:
  196. ;strictly speaking, we should remember the statistics, but I'm not going to now.
  197. f_terminate:
  198. f_reset_interface:
  199. f_release_type:
  200.     mov    bx,(size handle_struc)
  201.     call    queue_advance
  202.     mov    ax,saved_bx
  203.     mov    [bx].event_handle,ax
  204.     call    do_their_isr
  205.     jmp    our_isr_done
  206.  
  207. f_access_type:
  208.     mov    bx,(size at_struc)
  209.     call    queue_advance
  210.     mov    al,saved_al
  211.     mov    [bx].at_if_class,al
  212.     mov    ax,saved_bx
  213.     mov    [bx].at_if_type,ax
  214.     mov    [bx].at_if_number,dl
  215.     mov    [bx].at_typelen,cx
  216.     mov    their_recv.segm,es
  217.     mov    their_recv.offs,di
  218.     mov    ax,cs            ;and stick our receiver in.
  219.     mov    es,ax
  220.     mov    di,offset our_recv
  221.     call    do_their_isr
  222.     jc    f_access_type_1
  223.     mov    ax,saved_ax
  224.     mov    [bx].at_handle,ax
  225. f_access_type_1:
  226.     les    di,their_recv        ;now restore ds and si.
  227.     jmp    our_isr_done
  228.  
  229.  
  230. f_send_pkt:
  231.     mov    bx,(size event_struc)
  232.     add    bx,cx
  233.     call    queue_advance
  234. ;make a copy of their packet.
  235.     push    cx
  236.     push    si
  237.     push    di
  238.     push    ds
  239.     push    es
  240.     lea    di,[bx] + (size event_struc)
  241.     mov    ax,cs
  242.     mov    es,ax
  243.     mov    ds,saved_ds
  244.     rep    movsb
  245.     pop    es
  246.     pop    ds
  247.     pop    di
  248.     pop    si
  249.     pop    cx
  250.     call    do_their_isr
  251.     jmp    our_isr_done
  252.  
  253. f_get_address:
  254.     mov    bx,(size ga_struc)
  255.     add    bx,cx
  256.     call    queue_advance
  257.     mov    ax,saved_bx        ;save their handle
  258.     mov    [bx].ga_handle,ax
  259.     call    do_their_isr
  260.     jc    f_get_address_1
  261. ;make a copy of their address.
  262.     mov    [bx].ga_length,cx    ;we need to save this because it
  263.                     ;might be less than the total allocated.
  264.     push    cx
  265.     push    si
  266.     push    di
  267.     push    ds
  268.     push    es
  269.     mov    si,di            ;get es:di into ds:si
  270.     mov    ax,es
  271.     mov    ds,ax
  272.     lea    di,[bx] + (size ga_struc)    ;get our pointer into es:di.
  273.     mov    ax,cs
  274.     mov    es,ax
  275.     rep    movsb
  276.     pop    es
  277.     pop    ds
  278.     pop    di
  279.     pop    si
  280.     pop    cx
  281. f_get_address_1:
  282.     jmp    our_isr_done
  283.  
  284.  
  285. f_set_rcv_mode:
  286. f_get_rcv_mode:
  287. set_multicast_list:
  288. get_multicast_list:
  289. f_set_address:
  290.     mov    bx,(size event_struc)
  291.     call    queue_advance
  292.     call    do_their_isr
  293.  
  294. our_isr_done:
  295.     push    saved_f            ;restore their flags, see [2]
  296.     popf
  297.  
  298.     mov    ax,saved_ax
  299.     mov    bx,saved_bx
  300.     mov    ds,saved_ds        ;restore the two registers we destroyed.
  301.     assume    ds:nothing
  302.  
  303.     retf    2            ;return, popping their old flags, [2].
  304.     assume    ds:code
  305.  
  306.  
  307. ;do_their_isr executes their isr with the original registers.
  308. ;called with all their registers except f, ds, and bx.
  309. ;exits with all their registers except ds, ax, and bx.  bx is queue_ptr
  310. do_their_isr:
  311.  
  312. ;setup their context.
  313.     mov    ax,saved_f        ;restore their flags, see [1]
  314.     and    ax,not 200h        ;clear the interrupt flag, as required
  315.     push    ax            ;  when faking an interrupt.
  316.     mov    ax,saved_ax
  317.     mov    bx,saved_bx
  318.     mov    ds,saved_ds        ;restore the two registers we destroyed.
  319.     assume    ds:nothing
  320.  
  321. ;    [1] we pushed the flags earlier.
  322.     call    their_isr        ;now fake their interrupt.
  323.  
  324. ;save their context.
  325.     mov    saved_ax,ax        ;save the new registers.
  326.     mov    saved_bx,bx
  327.     mov    saved_ds,ds
  328.     mov    ax,cs            ;set up a pointer to the next event.
  329.     mov    ds,ax
  330.     assume    ds:code
  331.     pushf                ;save the new flags.
  332.     pop    ax
  333.     and    saved_f,200h        ;merge the interrupt flag in saved_f
  334.     or    saved_f,ax        ;  with the new flags.
  335.  
  336. ;remember whether it succeeded or not.
  337.     mov    bx,queue_ptr
  338.     mov    [bx].event_error,NO_ERROR    ;assume that all was okay.
  339.     jnc    our_isr_1
  340.     mov    [bx].event_error,dh    ;it wasn't.
  341. our_isr_1:
  342.     ret
  343.  
  344.  
  345. their_recv    dd    ?
  346.  
  347. our_recv:
  348.     assume    ds:nothing
  349.     mov    saved_ds,ds
  350.     mov    saved_bx,bx
  351.     mov    saved_ax,ax
  352.     cld
  353.  
  354.     mov    bx,cs            ;set up ds.
  355.     mov    ds,bx
  356.     assume    ds:code
  357.  
  358.     or    ax,ax            ;first call or second?
  359.     je    our_recv_first
  360.  
  361.     mov    bx,(size handle_struc)
  362.     add    bx,cx
  363.     call    queue_advance
  364.     mov    [bx].event_function,EVENT_RECEIVE    ;not a real function.
  365.     mov    [bx].event_error,0        ;no errors possible.
  366.     mov    ax,saved_bx
  367.     mov    [bx].event_handle,ax    ;remember which handle it was.
  368.  
  369.     push    cx
  370.     push    si
  371.     push    di
  372.     push    ds
  373.     push    es
  374.     lea    di,[bx] + (size handle_struc)
  375.     mov    ax,cs
  376.     mov    es,ax
  377.     mov    ds,saved_ds
  378.     rep    movsb
  379.     pop    es
  380.     pop    ds
  381.     pop    di
  382.     pop    si
  383.     pop    cx
  384.  
  385.     jmp    short our_recv_done
  386. our_recv_first:
  387. ;ignore the first upcall.
  388. our_recv_done:
  389.     mov    ax,saved_ax
  390.     mov    bx,saved_bx
  391.     mov    ds,saved_ds        ;restore the two registers we destroyed.
  392.     assume    ds:nothing
  393.  
  394.     jmp    their_recv
  395.     assume    ds:code
  396.  
  397.  
  398. queue_advance:
  399. ;enter with bx = number of bytes that we require in the queue.
  400. ;exit with bx,queue_ptr set to a pointer to our entry.
  401. ;preserve everything but ds, bx, and the flags.
  402. queue_advance_3:
  403.     mov    ax,queue_tail
  404.     add    ax,bx
  405.     cmp    ax,queue_head        ;if we don't overlap the head, we're
  406.     jbe    queue_advance_1        ;  okay.
  407.  
  408.     xchg    bx,queue_head        ;get queue_head and save bx.
  409.     add    bx,[bx].event_length
  410.     cmp    bx,queue_end        ;see if we hit the end.
  411.     xchg    queue_head,bx        ;store queue_head and restore bx.
  412.     jb    queue_advance_3        ;if we're less than the end, continue.
  413. ;we have to wrap here.
  414.     push    bx
  415.     mov    bx,queue_tail
  416.     mov    ax,queue_end        ;make an event length that's too large.
  417.     sub    ax,offset queue_begin
  418.     mov    [bx].event_length,ax
  419.     mov    bx,offset queue_begin    ;and restart from the beginning.
  420.     mov    queue_tail,bx        ;ensure that we nuke some more.
  421.     mov    queue_head,bx
  422.     pop    bx
  423.     jmp    queue_advance_3
  424. queue_advance_1:
  425.     xchg    ax,queue_tail        ;update the tail and get this ptr.
  426.     mov    queue_ptr,ax        ;save this pointer.
  427.     xchg    ax,bx
  428.     mov    [bx].event_length,ax    ;store the length of this entry here.
  429.  
  430.     mov    ah,saved_ah        ;store their function value.
  431.     mov    [bx].event_function,ah
  432.  
  433. ;remember when it happened.
  434.     push    dx
  435.     push    ds
  436.     mov    ax,40h
  437.     mov    ds,ax
  438.     mov    ax,ds:6ch        ;get the timer tick count.
  439.     mov    dx,ds:6eh
  440.     pop    ds
  441.     mov    [bx].event_time+0,ax
  442.     mov    [bx].event_time+2,dx
  443.     pop    dx
  444.  
  445.     ret
  446.  
  447.  
  448. copyleft_msg    label    byte
  449.  db "Packet driver tracer version ",majver+'0','.',version+'0'," copyright 1988-89, Russell Nelson.",CR,LF
  450.  db "This program is free software; see the file COPYING for details.",CR,LF
  451.  db "NO WARRANTY; see the file COPYING for details.",CR,LF
  452. crlf_msg    db    CR,LF,'$'
  453.  
  454. packet_int_no_name    db    "Packet interrupt number ",'$'
  455. buffer_size_name    db    "Buffer size ",'$'
  456.  
  457. before_exec_msg    db    "Now run your network software and type 'exit' when finished",CR,LF,'$'
  458. run_dump_msg    db    "Now run 'dump' to interpret 'trace.out'",CR,LF,'$'
  459.  
  460. disk_full_msg    db    "Disk Full!",'$'
  461.  
  462. already_msg    db    CR,LF,"There is no packet driver at ",'$'
  463. packet_int_msg    db    CR,LF
  464.         db    "Error: <packet_int_no> should be in the range 0x60 to 0x80"
  465.         db    '$'
  466.  
  467. usage_msg    db    "usage: trace packet_int_no <buffer_size>",'$'
  468.  
  469. queue_error_msg    db    "Error: <buffer_size> should be larger than 2000 and less than 64000",'$'
  470.  
  471. trace_out    db    "TRACE.OUT",0    ;filename that we write the dump to.
  472.  
  473. HT    equ    09h
  474. CR    equ    0dh
  475. LF    equ    0ah
  476.  
  477. usage_error:
  478.     mov    dx,offset usage_msg
  479. error:
  480.     mov    ah,9
  481.     int    21h
  482.     int    20h
  483.  
  484. already_error:
  485.     mov    dx,offset already_msg
  486.     mov    di,offset packet_int_no
  487.     call    print_number
  488.     int    20h
  489.  
  490. start_1:
  491.     mov    sp,offset stack
  492.  
  493.     mov    dx,offset copyleft_msg
  494.     mov    ah,9
  495.     int    21h
  496.  
  497.     mov    si,offset phd_dioa+1
  498.     cmp    byte ptr [si],CR    ;end of line?
  499.     je    usage_error
  500.  
  501.     mov    di,offset packet_int_no    ;parse the packet interrupt number
  502.     mov    bx,offset packet_int_no_name
  503.     call    get_number        ;  for them.
  504.  
  505.     mov    di,offset queue_length    ;parse the packet interrupt number
  506.     mov    bx,offset buffer_size_name
  507.     call    get_number        ;  for them.
  508.  
  509.     cmp    byte ptr [si],CR    ;end of line?
  510.     jne    usage_error
  511.  
  512.     cmp    queue_length+2,0
  513.     jne    start_3
  514.     cmp    queue_length,2000
  515.     ja    start_2
  516. start_3:
  517.     mov    dx,offset queue_error_msg
  518.     jmp    error
  519. start_2:
  520.  
  521. ;initialize the queue
  522.     mov    bx,offset queue_begin
  523.     mov    queue_tail,bx
  524.     add    bx,queue_length
  525.     mov    queue_end,bx        ;initialize the head of the queue.
  526.     mov    [bx].event_length,1    ;anything >0 will ensure that we're >end.
  527.     mov    queue_head,bx
  528.  
  529. ;do some error checking.
  530.     mov    dx,offset packet_int_msg;make sure that the packet interrupt
  531.     cmp    packet_int_no,60h    ;  number is in range.
  532.     jb    error
  533.     cmp    packet_int_no,80h
  534.     ja    error
  535.  
  536.     mov    ah,35h            ;get their packet interrupt.
  537.     mov    al,packet_int_no
  538.     int    21h
  539.  
  540.     lea    di,3[bx]        ;see if there is already a signature
  541.     mov    si,offset signature    ;  there.
  542.     mov    cx,signature_len
  543.     repe    cmpsb
  544.     je    start_4            ;yes, so we can trace it.
  545.     jmp    already_error        ;no, give them an error.
  546. start_4:
  547.  
  548.     mov    ah,35h            ;remember their packet interrupt.
  549.     mov    al,packet_int_no
  550.     int    21h
  551.     mov    their_isr.offs,bx
  552.     mov    their_isr.segm,es
  553.  
  554.     mov    ah,25h            ;install our packet interrupt
  555.     mov    dx,offset our_isr
  556.     int    21h
  557.  
  558.     mov    dx,offset before_exec_msg
  559.     mov    ah,9
  560.     int    21h
  561.  
  562. ;
  563. ; Now free the memory we don't need.
  564. ;
  565.     mov    bx,queue_end
  566.     add    bx,size event_struc    ;leave room for one more.
  567.     add    bx,0fh            ;round up to next highest paragraph.
  568.     mov    cl,4
  569.     shr    bx,cl
  570.     push    cs
  571.     pop    es
  572.     mov    ah,4ah
  573.     int    21h
  574.  
  575. ; Now we execute command.com
  576.  
  577.     mov    ax,cs
  578.     mov    word ptr parm2+2,ax
  579.     mov    word ptr parm3+2,ax
  580.     mov    word ptr parm4+2,ax
  581.  
  582.     mov    si,offset their_dioa+1    ;re-parse the two fcbs.
  583.     mov    di,offset phd_fcb1
  584.     push    ds
  585.     pop    es
  586.     mov    ax,2901h
  587.     int    21h
  588.  
  589.     mov    di,offset phd_fcb2
  590.     mov    ax,2901h
  591.     int    21h
  592.  
  593.     mov    si,offset comspec_env_str    ;see if this is the one.
  594.     mov    cx,comspec_env_len
  595.     mov    di,offset program
  596.     call    getenv
  597.  
  598.     mov    ah,4bh
  599.     mov    bx,offset parm
  600.     mov    dx,offset program
  601.     mov    al,0
  602.     int    21h
  603.  
  604.     mov    bx,cs            ;restore our segment registers.
  605.     mov    ds,bx
  606.     mov    es,bx
  607.     mov    ss,bx
  608.     mov    sp,offset stack
  609.  
  610. ; Give up our packet interception.
  611.  
  612.     mov    al,packet_int_no    ;release our_isr.
  613.     mov    ah,25h
  614.     push    ds
  615.     lds    dx,their_isr
  616.     int    21h
  617.     pop    ds
  618.  
  619. ; Now we write our captured information out to disk.
  620.  
  621.     mov    dx,offset trace_out    ;create "trace.out".
  622.     mov    ah,3ch
  623.     mov    cx,0
  624.     int    21h
  625.  
  626.     mov    bx,ax
  627.  
  628.     mov    si,queue_head
  629. write_out:
  630.     mov    dx,si
  631.     mov    cx,[si].event_length    ;write this event out.
  632.     add    si,cx            ;is this the end of the queue?
  633.     cmp    si,queue_end        ;
  634.     ja    write_out_1
  635.  
  636.     mov    ah,40h
  637.     int    21h
  638.     cmp    ax,cx
  639.     jne    write_out_full
  640.  
  641.     jmp    write_out
  642.  
  643. write_out_1:
  644.     mov    si,offset queue_begin    ;yes.
  645. write_out_2:
  646.     cmp    si,queue_tail        ;quit when we hit the tail.
  647.     jae    write_out_3
  648.  
  649.     mov    dx,si            ;set dx for the file write below.
  650.     mov    cx,[si].event_length    ;write this event out.
  651.     add    si,cx
  652.  
  653.     mov    ah,40h
  654.     int    21h
  655.     cmp    ax,cx
  656.     jne    write_out_full
  657.  
  658.     jmp    write_out_2
  659. write_out_3:
  660.     mov    ah,3eh            ;close the file.
  661.     int    21h
  662.  
  663.     mov    dx,offset run_dump_msg
  664.     mov    ah,9
  665.     int    21h
  666.  
  667.     int    20h
  668.  
  669. write_out_full:
  670.     mov    ah,9
  671.     mov    dx,offset disk_full_msg
  672.     int    21h
  673.     int    20h
  674.  
  675.  
  676. get_number:
  677.     mov    bp,10            ;we default to 10.
  678.     jmp    short get_number_0
  679.  
  680. get_hex:
  681.     mov    bp,16
  682. ;get a hex number, skipping leading blanks.
  683. ;enter with si->string of digits,
  684. ;    bx -> dollar terminated name of number,
  685. ;    di -> dword to store the number in.  [di] is not modified if no
  686. ;        digits are given, so it acts as the default.
  687. ;return cy if there are no digits at all.
  688. ;return nc, bx:cx = number, and store bx:cx at [di].
  689. get_number_0:
  690.     push    bx            ;remember the name of this number.
  691.     call    skip_blanks
  692.     call    get_digit        ;is there really a number here?
  693.     jc    get_number_3
  694.     or    al,al            ;Does the number begin with zero?
  695.     jne    get_number_4        ;no.
  696.     mov    bp,8            ;yes - they want octal.
  697. get_number_4:
  698.  
  699.     xor    cx,cx            ;get a hex number.
  700.     xor    bx,bx
  701. get_number_1:
  702.     lodsb
  703.     cmp    al,'x'            ;did they really want hex?
  704.     je    get_number_5        ;yes.
  705.     cmp    al,'X'            ;did they really want hex?
  706.     je    get_number_5        ;yes.
  707.     call    get_digit        ;convert a character into an int.
  708.     jc    get_number_2        ;not a digit (neither hex nor dec).
  709.     xor    ah,ah
  710.     cmp    ax,bp            ;larger than our base?
  711.     jae    get_number_2        ;yes.
  712.  
  713.     push    ax            ;save the new digit.
  714.  
  715.     mov    ax,bp            ;multiply the low word by ten.
  716.     mul    cx
  717.     mov    cx,ax            ;keep the low word.
  718.     push    dx            ;save the high word for later.
  719.     mov    ax,bp
  720.     mul    bx
  721.     mov    bx,ax            ;we keep only the low word (which is our high word)
  722.     pop    dx
  723.     add    bx,ax            ;add the high result from earlier.
  724.  
  725.     pop    ax            ;get the new digit back.
  726.     add    cx,ax            ;add the new digit in.
  727.     adc    bx,0
  728.     jmp    get_number_1
  729. get_number_5:
  730.     mov    bp,16            ;change the base to hex.
  731.     jmp    get_number_1
  732. get_number_2:
  733.     dec    si
  734.     mov    [di],cx            ;store the parsed number.
  735.     mov    [di+2],bx
  736.     clc
  737.     jmp    short get_number_6
  738. get_number_3:
  739.     stc
  740. get_number_6:
  741.     pop    dx            ;get the name of the number back.
  742.  
  743.     pushf                ;save some stuff.
  744.     push    bx
  745.     push    cx
  746.     push    si
  747.     push    di
  748.     call    print_number
  749.     pop    di
  750.     pop    si
  751.     pop    cx
  752.     pop    bx
  753.     popf
  754.     ret
  755.  
  756.  
  757. print_number:
  758. ;enter with dx -> dollar terminated name of number, di ->dword.
  759. ;exit with the number printed and the cursor advanced to the next line.
  760.     mov    ah,9            ;print the name of the number.
  761.     int    21h
  762.     mov    al,'0'
  763.     call    chrout
  764.     mov    al,'x'
  765.     call    chrout
  766.     mov    ax,[di]            ;print the number in hex.
  767.     mov    dx,[di+2]
  768.     call    hexout
  769.     mov    al,' '
  770.     call    chrout
  771.     mov    al,'('
  772.     call    chrout
  773.     mov    ax,[di]            ;print the number in decimal.
  774.     mov    dx,[di+2]
  775.     call    decout
  776.     mov    al,')'
  777.     call    chrout
  778.     mov    al,CR
  779.     call    chrout
  780.     mov    al,LF
  781.     call    chrout
  782.     ret
  783.  
  784.  
  785. skip_blanks:
  786.     lodsb                ;skip blanks.
  787.     cmp    al,' '
  788.     je    skip_blanks
  789.     cmp    al,HT
  790.     je    skip_blanks
  791.     dec    si
  792.     ret
  793.  
  794.  
  795. get_digit:
  796. ;enter with al = character
  797. ;return nc, al=digit, or cy if not a digit.
  798.     cmp    al,'0'            ;decimal digit?
  799.     jb    get_digit_1        ;no.
  800.     cmp    al,'9'            ;. .?
  801.     ja    get_digit_2        ;no.
  802.     sub    al,'0'
  803.     clc
  804.     ret
  805. get_digit_2:
  806.     or    al,20h
  807.     cmp    al,'a'            ;hex digit?
  808.     jb    get_digit_1
  809.     cmp    al,'f'            ;hex digit?
  810.     ja    get_digit_1
  811.     sub    al,'a'-10
  812.     clc
  813.     ret
  814. get_digit_1:
  815.     stc
  816.     ret
  817.  
  818.  
  819. hexout:
  820.     mov    cl,'0'            ;prepare to eliminate leading zeroes.
  821.     xchg    ax,dx            ;just output 32 bits in hex.
  822.     call    wordout            ;output dx.
  823.     xchg    ax,dx
  824.     jmp    wordout            ;output ax.
  825.  
  826. decout:
  827.     mov    si,ax            ;get the number where we want it.
  828.     mov    di,dx
  829.  
  830.     xor    ax,ax            ;start with all zeroes in al,bx,bp
  831.     mov    bx,ax
  832.     mov    bp,ax
  833.  
  834.     mov    cx,32            ;32 bits in two 16 bit registers.
  835. decout_1:
  836.     shl    si,1
  837.     rcl    di,1
  838.     xchg    bp,ax
  839.     call    addbit
  840.     xchg    bp,ax
  841.     xchg    bx,ax
  842.     call    addbit
  843.     xchg    bx,ax
  844.     adc    al,al
  845.     daa
  846.     loop    decout_1
  847.  
  848.     mov    cl,'0'            ;prepare to eliminate leading zeroes.
  849.     call    byteout            ;output the first two.
  850.     mov    ax,bx            ;output the next four
  851.     call    wordout            ;output the next four
  852.     mov    ax,bp
  853. wordout:
  854.     push    ax
  855.     mov    al,ah
  856.     call    byteout
  857.     pop    ax
  858. byteout:
  859.     mov    ah,al
  860.     shr    al,1
  861.     shr    al,1
  862.     shr    al,1
  863.     shr    al,1
  864.     call    digout
  865.     mov    al,ah
  866. digout:
  867.     and    al,0fh
  868.     add    al,90h    ;binary digit to ascii hex digit.
  869.     daa
  870.     adc    al,40h
  871.     daa
  872.     cmp    al,cl            ;leading zero?
  873.     je    return
  874.     mov    cl,-1            ;no more leading zeros.
  875. chrout:
  876.     push    ax            ;print the char in al.
  877.     xchg    al,dl
  878.     mov    ah,2
  879.     int    21h
  880.     xchg    al,dl
  881.     pop    ax
  882. return:
  883.     ret
  884.  
  885.  
  886. addbit:    adc    al,al
  887.     daa
  888.     xchg    al,ah
  889.     adc    al,al
  890.     daa
  891.     xchg    al,ah
  892.     ret
  893.  
  894.  
  895. getenv:
  896. ;enter with ds:si -> environment string to look for, cx = length of string,
  897. ;  ds:di -> place to put the string's value.
  898.     push    es
  899.     push    di            ;remember where we're supposed to put it.
  900.  
  901.     mov    es,phd_env        ;search the environment.
  902.     xor    di,di
  903.  
  904. getenv_2:
  905.     cmp    byte ptr es:[di],0    ;see if we're at the end.
  906.     je    getenv_0
  907.  
  908.     push    cx
  909.     push    si
  910.     push    di
  911.     repe    cmpsb
  912.     pop    di
  913.     pop    si
  914.     je    getenv_3
  915.     mov    cx,-1            ;skip to the next null.
  916.     xor    al,al
  917.     repne    scasb
  918.     pop    cx
  919.     jmp    getenv_2
  920. getenv_3:
  921. ;copy the environment string to current_dir.
  922.     pop    cx
  923.     add    di,cx            ;go to the end of the string.
  924.     pop    si            ;pushed as di, -> place to put the string.
  925. getenv_4:
  926.     mov    al,es:[di]
  927.     mov    [si],al
  928.     inc    di
  929.     inc    si
  930.     or    al,al
  931.     jne    getenv_4
  932.     dec    si            ;point si to the null again.
  933.     pop    es
  934.     clc
  935.     ret
  936. getenv_0:
  937.     add    sp,2
  938.     pop    es
  939.     stc
  940.     ret
  941.  
  942.  
  943. end_code    label    byte
  944.  
  945. queue_begin    label    byte
  946.  
  947. code    ends
  948.  
  949.     end    start
  950.